//
// Created by denglibin on 19-4-10.
//

/**
 *应用程序常常需要在多于一个文件描述符上阻塞，例如响应键盘输入(stdin),进程间通信以及同时操作多个文件。
 * 基于事件驱动机制的图形用户界面(GUI)应用的主循环中可能包含上百个等待响应的事件。
 * 在不使用线程，尤其是独立处理每一个文件的情况下，进程无法在多个文件描述符上同时阻塞。
 * IO多路复用允许应用在多个文件描述符上同时阻塞，并在其中莫个可以读写时收到通知。
 * 一般来讲IO多路复用的设计遵循以下原则：
 *  1、当任何文件描述符准备好IO时告诉我。
 *  2、在一个或更多文件描述符就绪前处于睡眠状态
 *  3、唤醒：哪个准备好了
 *  4、在不阻塞的情况下处理所有IO就绪的文件描述符
 *  5、返回第一步，重新开始。
 *
 *  Linux提供了三种IO多路服用的方案： select, poll, epoll
 *
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);

 监测的文件描述符可以分为三类，分别等待不同的事件。
 监测readfds集合中的文件描述符，确认其中是否有可读数据。
 监测writefds集合中的文件描述符，确认是否有一个写操作可以不阻塞的完成。
 监测exceptefds中的文件描述符，确认其中是否有异常发生或出现外带(out-of-band)数据(这种情况只适用套接字)
 指定的集合可以为NULL,相应的select()则不对此类进行监控

 成功返回时，每个集合只包含对应类型IO就绪的文件描述符。
 举例：readfds集合中有两个文件描述符：7和9。当调用返回时，如果7还在集合中，该文件描述符就准备好进行无阻塞IO了
 如果9不在集合中，它可能在被读取时发生了阻塞。

 第一个参数 n 等于所有集合中文件描述符的最大值加1.这样，select()调用者需要找到最大文件描述符的值，并且加一后传给第一个参数。

   FD_ZERO()从指定集合中移除所有文件描述符，在每次调用select()之前需要调用该宏
    fd_set writefds;
    FD_ZERO(&writefds);
   FD_SET(fd, &writefds)：向指定集合中添加文件描述符
   FD_CLR(fd, &writefds): 从指定集合中移除文件描述符，设计良好的代码应该不使用该宏，
   FD_ISSET 测试一个文件描述符在不在给定集合中，如果在则返回一个非0值，否则用0表示不在。
             一般在select返回后用来检查一个文件描述符是否就绪。
   成功时返回 三个集合中IO就绪的文件描述符的数目。如果给出了时限，返回值可能是0。错误返回-1，并设置errno
 */
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define TIME_OUT 5 //定义select超时时间 5秒
#define  BUF_LEN 1024

static int main_(void){
    struct timeval tv;
    fd_set readfds; //读操作的文件描述符集合
    int ret;
    FD_ZERO(&readfds);
    //添加标准输入的文件描述到集合
    FD_SET(STDIN_FILENO, &readfds);
    tv.tv_sec = TIME_OUT;
    tv.tv_usec = 0;
    //select 阻塞
    ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
    //调用返回说明有文件描述符io已就绪，或者超时了

    if(ret == -1){
        perror("select");
        return -1;
    }else if(ret == 0){
        printf("超时了");
        return -1;
    }
    //如果标准输入的文件描述符已就绪
    if(FD_ISSET(STDIN_FILENO, &readfds)){
        char buf[BUF_LEN+1];
        int len;
        len = read(STDIN_FILENO, buf, BUF_LEN);
        if(len == -1){
            perror("read");
            return -1;
        }
        if(len){
            buf[len] = '\0';
            printf("read: %s\n", buf);
            return 0;
        }
    }
    fprintf(stderr, "This should not happen!\n");
    return -1;
}













